package com.qf.controller;

import com.alibaba.fastjson.JSON;
import com.qf.mapper.StudentMapper;
import com.qf.pojo.Student;
import io.lettuce.core.RedisClient;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/stu")
public class StudentController implements InitializingBean {

    @Autowired
    private RedissonClient client;

    private RBloomFilter<Long> stuBloom;

    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    //将数据库中的id值存入到布隆过滤器.简单模拟
    @Override
    public void afterPropertiesSet() throws Exception {
        //初始化布隆过滤器
        stuBloom = client.getBloomFilter("stuBloom");
        //错误率是10w分之1 (参数一：为位图的初始容量,参数二：为布隆过滤器判断的错误概率)
        stuBloom.tryInit(1000,0.01);

        //将数据库中的student表中存在的id加入到布隆过滤器
        List<Long> ids = studentMapper.findIds();
        for (Long id : ids) {
            stuBloom.add(id);
        }
    }


    @GetMapping("/{id}")
    public Student findOne(@PathVariable("id") Long id) throws InterruptedException {
        Student student = null;

        //解决缓存穿透方式二（推荐方式）
        if (stuBloom.contains(id)) {

            //todo：缓存过期时间是2s，这里休眠3s，模拟缓存过期。此时大量请求过来，都会打到数据库---缓存击穿
            //todo：保证即使缓存过期了，大量请求过来，也只能有一个打到数据库，剩下的都走缓存 -- 加锁（分布式锁）
            Thread.sleep(7000);

            //1.查询缓存
            String stuJson = redisTemplate.opsForValue().get("student:id:" + id);

            if (stuJson != null && stuJson.length() != 0) {
                //有缓存
                System.out.println("命中缓存....");
                student = JSON.parseObject(stuJson, Student.class);
            } else {
                //没有缓存-- 先查数据库+加入缓存
                //先查数据库
                //todo：加入分布式锁，解决缓存过期，导致的缓存击穿问题
                RLock lock = client.getLock("lock:id:" + id);
                try {
                    //锁
                    lock.lock();

                    stuJson = redisTemplate.opsForValue().get("student:id:" + id);

                    if (stuJson != null && stuJson.length() != 0) {
                        //有缓存
                        System.out.println("命中缓存....");
                        student = JSON.parseObject(stuJson, Student.class);

                    }else{
                        System.out.println("没有命中缓存，从数据库中查询数据....");
                        student = studentMapper.selectById(id);
                        //加入缓存
                        if (student != null) {
                            String json = JSON.toJSONString(student);
                            redisTemplate.opsForValue().set("student:id:" + id, json, 5, TimeUnit.SECONDS);
                        }
                    }
//            else{
//                //解决缓存穿透方式一，缓存空对象(不可取，会向redis中存入大量无意义的数据，攻击者大量访问这种数据，redis最后内存可能都不够用了)
//                redisTemplate.opsForValue().set("student:id:" + id, "null");
//            }
                }finally {
                    lock.unlock();
                }
            }
        }else{
            //数据库一定不存在这个id的student数据
            System.out.println("数据库一定不存在这个数据");
        }

        return student;
    }
}
